﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MadProps
{
    public abstract class PropOwner : IPropChangedListener, IPropChangedRaiser
    {
        protected static T BuildSchema<T>() { return PropFactory.BuildSchema<T>(); }

        readonly IList<IProp> props;
        readonly IPropChangedListener meAsListener;

        protected PropOwner()
            : this(Composition.Default())
        {
        }

        protected PropOwner(Composition composition)
        {
            meAsListener = this;

            foreach (var propId in PropIdCache.GetPropIds(this.GetType()))
            {
                Definition.MetadataStore.Build(propId, composition);
            }

            var list = new List<IProp>();
            PropFactory.CreatePropsFor(this, list);
            props = list.AsReadOnly();

            foreach (var prop in list)
            {
                this.Subscribe(prop);
            }
        }

        public IEnumerable<IProp> GetProps()
        {
            return props;
        }

        public IProp<T> GetProp<T>(IProp<T> template)
        {
            return props.OfType<IProp<T>>()
                .Single(p => p.PropId == template.PropId);
        }

        void IPropChangedListener.OnPropChanged(object sender, PropChangedEventArgs args)
        {
            if (args.HasVisited(this))
            {
                return;
            }

            if (this == args.Prop.Owner)
            {
                var cascade = args.OldValue as IPropChangedRaiser;
                if (cascade != null)
                {
                    cascade.PropChanged -= meAsListener.OnPropChanged;
                }
                cascade = args.NewValue as IPropChangedRaiser;
                if (cascade != null)
                {
                    cascade.PropChanged += meAsListener.OnPropChanged;
                }
            }

            PropChanged.Raise(sender, args);
            OnPropChanged(args);
        }

        void Subscribe(IPropChangedRaiser raiser)
        {
            raiser.PropChanged += meAsListener.OnPropChanged;

            var prop = raiser as IProp;
            if (prop != null && this == prop.Owner)
            {
                var cascade = prop.Value as IPropChangedRaiser;
                if (cascade != null)
                {
                    cascade.PropChanged += meAsListener.OnPropChanged;
                }
            }
        }

        void Unsubscribe(IPropChangedRaiser raiser)
        {
            raiser.PropChanged -= meAsListener.OnPropChanged;

            var prop = raiser as IProp;
            if (prop != null && this == prop.Owner)
            {
                var cascade = prop.Value as IPropChangedRaiser;
                if (cascade != null)
                {
                    cascade.PropChanged -= meAsListener.OnPropChanged;
                }
            }
        }

        protected virtual void OnPropChanged(PropChangedEventArgs args)
        {
        }

        public event EventHandler<PropChangedEventArgs> PropChanged;
    }
}
